package com.tengju.user.infrastructure.repository;

import com.google.common.collect.Lists;
import com.tengju.user.domain.model.archive.letax.IdentityType;
import com.tengju.user.domain.model.sign.*;
import com.tengju.user.domain.model.user.*;
import com.tengju.user.domain.service.UserDocService;
import com.tengju.user.domain.shared.BasePage;
import com.tengju.user.domain.shared.DateUtil;
import com.tengju.user.domain.shared.SpringEvent;
import com.tengju.user.infrastructure.persistence.mybatis.dataobject.SignStatisticsDetailDO;
import com.tengju.user.infrastructure.persistence.mybatis.dataobject.StarBlacklistDO;
import com.tengju.user.infrastructure.persistence.mybatis.dataobject.StarChangeNoticeRecordDO;
import com.tengju.user.infrastructure.persistence.mybatis.dataobject.StarSignInfoDO;
import com.tengju.user.infrastructure.persistence.mybatis.mapper.data.StarChangeNoticeRecordCustomMapper;
import com.tengju.user.infrastructure.persistence.mybatis.mapper.dealer.custom.StarBlacklistDOCustomMapper;
import com.tengju.user.infrastructure.persistence.mybatis.mapper.dealer.custom.StarSignInfoDOCustomMapper;
import com.tengju.user.infrastructure.persistence.mybatis.mapper.dealer.generate.StarSignInfoDOMapper;
import com.tengju.user.infrastructure.repository.convert.StarSignConvert;
import com.tengju.user.infrastructure.repository.convert.UserConvert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Repository
@Slf4j
public class StarSignInfoRepositoryImpl implements StarSignInfoRepository, ApplicationContextAware {

    @Resource
    StarSignInfoDOCustomMapper starSignInfoDOCustomMapper;

    @Resource
    StarSignInfoDOMapper starSignInfoDOMapper;

    @Resource
    StarBlacklistDOCustomMapper starBlacklistDOCustomMapper;

    @Autowired
    StarContractRepository starContractRepository;

    @Autowired
    StarChangeRecordRepository starChangeRecordRepository;

    @Autowired
    StarChangeNoticeRecordCustomMapper starChangeNoticeRecordCustomMapper;

    @Autowired
    UserDocService userDocService;

    private static final Set<Integer> testIdCodes = new HashSet<>(Arrays.asList(201022,
            6650043,
            6246824,
            4892062,
            2126092,
            1597470,
            989173,
            989169,
            989162,
            989160,
            541649,
            419204,
            419195,
            302477,
            269040,
            269037,
            257648,
            237487,
            237486,
            237485,
            224726,
            222826,
            204363,
            204125,
            200826,
            201272,
            201188,
            200361,
            200361));

    private ApplicationContext applicationContext;

    @Override
    public StarSignInfo getById(SignId signId) {
        if (signId == null) {
            return null;
        }
        StarSignInfoDO starSignInfoDO = starSignInfoDOCustomMapper.selectById(signId.getValue());
        return StarSignConvert.signInfoDOToInfo(starSignInfoDO);
    }

    @Override
    public StarSignInfo getByIdCode(UserIdCode idCode) {
        if (idCode == null) {
            return null;
        }
        StarSignInfoDO starSignInfoDO = starSignInfoDOCustomMapper
                .selectByIdCodeNotStep(idCode.getValue(),List.of(StarSignStepEnum.END,StarSignStepEnum.TERMINATED));
        return StarSignConvert.signInfoDOToInfo(starSignInfoDO);
    }

    @Override
    public StarSignInfo getByIdCodeAndBeforeLevel(UserIdCode idCode, StarLevelEnum beforeStarLevel) {
        if (idCode == null || beforeStarLevel == null) {
            return null;
        }
        StarSignInfoDO starSignInfoDO = starSignInfoDOCustomMapper
                .selectByIdCodeBeforeLevelNotStep(idCode.getValue(),beforeStarLevel.getCode(),List.of(StarSignStepEnum.END,StarSignStepEnum.TERMINATED));
        return StarSignConvert.signInfoDOToInfo(starSignInfoDO);
    }

    public List<StarSignInfo> getAllByIdCode(UserIdCode idCode) {
        if (idCode == null) {
            return Lists.newArrayList();
        }
        List<StarSignInfoDO> list = starSignInfoDOCustomMapper.selectAllByIdCode(idCode.getValue());
        return list.stream()
                .map(StarSignConvert::signInfoDOToInfo)
                .collect(Collectors.toList());
    }

    @Override
    public StarSignInfo getNewSignInfo(UserIdCode idCode) {
        if (idCode == null) {
            return null;
        }
        StarSignInfoDO starSignInfoDO = starSignInfoDOCustomMapper
                .selectByIdCode(idCode.getValue());
        return StarSignConvert.signInfoDOToInfo(starSignInfoDO);
    }

    @Override
    public List<StarSignInfo> getHistoryByIdCode(UserIdCode idCode) {
        if (idCode == null) {
            return Collections.emptyList();
        }
        List<StarSignInfoDO> starSignInfoDOList = starSignInfoDOCustomMapper.getHistoryByIdCode(idCode.getValue());
        return starSignInfoDOList.stream()
                .map(StarSignConvert::signInfoDOToInfo)
                .collect(Collectors.toList());
    }

    @Override
    public List<StarSignInfo> batchGetByIdCodes(List<UserIdCode> idCodes) {
        if (CollectionUtils.isEmpty(idCodes)) {
            return Collections.emptyList();
        }
        List<StarSignInfoDO> starSignInfoDOList = starSignInfoDOCustomMapper.batchGetByIdCodes(
                idCodes.stream().map(UserIdCode::getValue).collect(Collectors.toList())
        );
        return starSignInfoDOList.stream()
                .map(StarSignConvert::signInfoDOToInfo)
                .collect(Collectors.toList());
    }

    @Override
    public List<StarSignInfo> getSignStatusByIdCode(List<UserIdCode> idCodes) {
        if (CollectionUtils.isEmpty(idCodes)) {
            return Collections.emptyList();
        }
        List<Long> idCodeList = idCodes.stream().map(UserIdCode::getValue).collect(Collectors.toList());
        List<StarSignInfoDO> starSignInfoDOList = starSignInfoDOCustomMapper.selectSignStatusByIdCode(idCodeList);
        return starSignInfoDOList.stream()
                .map(StarSignConvert::signInfoDOToInfo)
                .collect(Collectors.toList());
    }

    @Override
    public List<StarSignInfo> batchGetByIdCodesSignStatus(List<UserIdCode> targetIdCodes, StarSignStatus status) {
        if (status == null) {
            return null;
        }
        List<StarSignInfoDO> starSignInfoDOList = starSignInfoDOCustomMapper
                .batchGetByIdCodesStepAuditStat(targetIdCodes, status.getStep(), status.getAuditStat());

        if (CollectionUtils.isEmpty(starSignInfoDOList)) {
            return null;
        }

        return starSignInfoDOList.stream()
                .map(StarSignConvert::signInfoDOToInfo)
                .collect(Collectors.toList());
    }

    @Override
    public void singleCasUpdate(StarSignInfo starSignInfo, Integer curStep) {
        if (starSignInfo == null) {
            return;
        }
        StarSignInfoDO starSignInfoDO = StarSignConvert.entityToDO(starSignInfo, null, new Date());
        int updateNum = starSignInfoDOCustomMapper.casUpdateByPrimaryKeySelective(starSignInfoDO, curStep);
        if (updateNum <= 0) {
            throw new StarSignException(String.format("签约邀请信息CAS更新失败,idCode:%s,updateStep:%s", starSignInfo.getIdCode().getValue(), starSignInfo.getStep()));
        }
        StarSignChangeEvent event = new StarSignChangeEvent(
                starSignInfo.getIdCode().getValue(),
                curStep,
                starSignInfo.getStep(),
                starSignInfo.getBeforeStarLevel(),
                starSignInfo.getStarLevel(),
                starSignInfo.getUpdateBy(),
                LocalDateTime.now()
        );
        applicationContext.publishEvent(new SpringEvent(event));
    }

    @Override
    public List<StarSignInfo> batchGetByIds(List<Long> idList) {
        if (CollectionUtils.isEmpty(idList)) {
            return Collections.emptyList();
        }
        List<StarSignInfoDO> starSignInfoDOList = starSignInfoDOCustomMapper.batchGetByIds(idList);
        return starSignInfoDOList.stream()
                .map(StarSignConvert::signInfoDOToInfo)
                .collect(Collectors.toList());
    }

    @Override
    public BasePage<StarSignInfo> listPageStarSignInfo(UserIdCode queryIdCode, String mobile, String nickName, String auditDesc, StarAuditStatEnum auditStatus, List<StarSignStepEnum> steps, Byte handleType, Integer originStarLevel, Integer targetStarLevel, Byte blackListFlag, Integer offset, Integer pageSize) {
        BasePage<StarSignInfo> result = new BasePage<>();
        UserInfo userInfo = userDocService.searchUserByIdCodeOrMobile(queryIdCode, mobile);
        if (userInfo != null) {
            List<StarSignInfo> allByIdCode = getAllByIdCode(userInfo.getIdCode());
            if (!CollectionUtils.isEmpty(allByIdCode)) {
                result.setDatas(allByIdCode);
                result.setTotal(allByIdCode.size());
                return result;
            }
        }
        if (queryIdCode != null || StringUtils.isNotBlank(mobile) || StringUtils.isNotBlank(nickName)) {
            return result;
        }
        List<StarSignInfoDO> starSignInfoDOList = starSignInfoDOCustomMapper.listStarSignInfoByClause(auditDesc, auditStatus, steps, handleType, originStarLevel, targetStarLevel, blackListFlag, offset, pageSize);
        List<StarSignInfo> starSignInfoList = starSignInfoDOList.stream()
                .map(StarSignConvert::signInfoDOToInfo)
                .collect(Collectors.toList());
        int total = starSignInfoDOCustomMapper.countStarSignInfoByClause(auditDesc, auditStatus, steps, handleType, originStarLevel, targetStarLevel, blackListFlag, offset, pageSize);
        result.setDatas(starSignInfoList);
        result.setTotal(total);
        return result;
    }

    @Override
    public BasePage<Long> listPageBlackUser(UserIdCode queryIdCode, String mobile, String nickName, Integer starLevel, Byte agreementStatus, LocalDateTime agreementFromDate, LocalDateTime agreementEndDate, Byte blackListFlag, Integer offset, Integer pageSize) {
        BasePage<Long> result = new BasePage<>();
        UserInfo userInfo = userDocService.searchUserByIdCodeOrMobile(queryIdCode, mobile);
        if (userInfo != null) {
            result.setDatas(Collections.singletonList(userInfo.getIdCode().getValue()));
            result.setTotal(1);
            return result;
        }
        if (queryIdCode != null || StringUtils.isNotBlank(mobile) || StringUtils.isNotBlank(nickName)) {
            return result;
        }
        List<Long> idCodes = starSignInfoDOCustomMapper.listAllUserByClause(
                null,
                null,
                nickName,
                starLevel,
                agreementStatus,
                agreementFromDate,
                agreementEndDate,
                blackListFlag,
                offset,
                pageSize
        );

        idCodes = idCodes.stream()
                .filter(Objects::nonNull)
                .map(UserConvert::decryptIdCode)
                .map(UserIdCode::getValue)
                .collect(Collectors.toList());

        int count = starSignInfoDOCustomMapper.countAllUserByClause(
                null,
                null,
                nickName,
                starLevel,
                agreementStatus,
                agreementFromDate,
                agreementEndDate,
                blackListFlag,
                offset,
                pageSize
        );
        result.setDatas(idCodes);
        result.setTotal(count);
        return result;
    }

    @Override
    public Integer countStep(Integer step) {
        return starSignInfoDOCustomMapper.countStep(step);
    }

    @Override
    public List<StarSignInfo> listStep(Integer step, long startId, Integer limit) {
        List<StarSignInfoDO> list = starSignInfoDOCustomMapper.listStep(step, startId, limit);
        return list.stream()
                .map(StarSignConvert::signInfoDOToInfo)
                .collect(Collectors.toList());
    }

    @Override
    public Integer countExpireRecord(LocalDateTime expireDate) {
        return starSignInfoDOCustomMapper.countExpireRecord(expireDate);
    }

    @Override
    public List<StarSignInfo> listExpireRecord(LocalDateTime expireDate, int limit) {
        List<StarSignInfoDO> starSignInfoDOList = starSignInfoDOCustomMapper.listExpireRecord(expireDate, limit);
        return starSignInfoDOList.stream()
                .map(StarSignConvert::signInfoDOToInfo)
                .collect(Collectors.toList());
    }

    @Override
    public void insert(StarSignInfo starSignInfo) {
        if (starSignInfo == null) {
            return;
        }
        Date now = new Date();
        StarSignInfoDO starSignInfoDO = StarSignConvert.entityToDO(starSignInfo, now, now);
        starSignInfoDOMapper.insertSelective(starSignInfoDO);
        StarSignChangeEvent event = new StarSignChangeEvent(
                starSignInfo.getIdCode().getValue(),
                0,
                starSignInfo.getStep(),
                starSignInfo.getBeforeStarLevel(),
                starSignInfo.getStarLevel(),
                starSignInfo.getUpdateBy(),
                LocalDateTime.now()
        );
        applicationContext.publishEvent(new SpringEvent(event));
    }

    @Override
    public IdentityType getIdentityType(UserIdCode idCode) {
        if (idCode == null) {
            return null;
        }

//        JsonResult<SignRes> signResJsonResult = iSignProvider.certificationIdentity(new SignQuery(Integer.valueOf(String.valueOf(idCode.getValue()))));
//
//        if(signResJsonResult != null && signResJsonResult.isSuccess()){
//            IdentityVO identityVO = StarSignConvert.signResToVo(signResJsonResult.getData());
//            return IdentityType.fromAccountType(identityVO.getIdentityType());
//        }

        return null;
    }

    @Override
    public int countStarNotice(LocalDateTime noticeDate, StarChangeNoticeTypeEnum noticeTypeEnum) {
        Integer noticeType = noticeTypeEnum != null ? noticeTypeEnum.getType() : null;
        return starChangeNoticeRecordCustomMapper.countStarNotice(noticeDate.toLocalDate(), noticeType);
    }

    @Override
    public List<StarChangeNoticeRecord> listStarNotice(LocalDateTime noticeDate, StarChangeNoticeTypeEnum noticeTypeEnum, Integer offset, Integer limit) {
        Integer noticeType = noticeTypeEnum != null ? noticeTypeEnum.getType() : null;
        List<StarChangeNoticeRecordDO> starChangeNoticeRecordDOList = starChangeNoticeRecordCustomMapper.listStarNotice(noticeDate.toLocalDate(), noticeType, offset, limit);
        return starChangeNoticeRecordDOList.stream()
                .map(StarSignConvert::noticeRecordDOToEntity)
                .collect(Collectors.toList());
    }

    @Override
    public List<UserStarInfo> listUserStarInfoByIdCodes(List<Long> idCodes) {
        if (CollectionUtils.isEmpty(idCodes)) {
            return Collections.emptyList();
        }
        List<UserInfo> userInfos = userDocService.searchUserByIdCodes(idCodes.stream()
                .filter(Objects::nonNull)
                .map(UserIdCode::new)
                .collect(Collectors.toList()));
        return listUserStarInfo(userInfos);
    }

    @Override
    public List<UserStarInfo> listUserStarInfo(List<UserInfo> userInfos) {
        if (CollectionUtils.isEmpty(userInfos)) {
            return Collections.emptyList();
        }
        List<UserIdCode> idCodes = userInfos.stream()
                .map(UserInfo::getIdCode)
                .collect(Collectors.toList());

        //获取等级变更记录
        List<StarChangeRecord> starChangeRecordDOList = starChangeRecordRepository.batchGetLastChangeRecord(idCodes);

        Map<UserIdCode, LocalDateTime> levelChangeTimeMap = starChangeRecordDOList.stream()
                .collect(Collectors.toMap(StarChangeRecord::getIdCode, StarChangeRecord::getChangeTime, (v1, v2) -> v1));

        //获取邀请信息
        List<StarSignInfo> starSignInfoList = batchGetByIdCodes(idCodes);
        Map<UserIdCode, StarSignInfo> signMap = starSignInfoList.stream()
                .collect(Collectors.toMap(StarSignInfo::getIdCode, v -> v, (v1, v2) -> v1));

        //黑名单信息
        List<StarBlacklistDO> starBlacklistDOList = starBlacklistDOCustomMapper.listStarBlackByIdCodeList(
                idCodes.stream().filter(Objects::nonNull).map(UserIdCode::getValue).collect(Collectors.toList())
        );
        Map<UserIdCode, StarLimitInfo> starLimitInfoMap = starBlacklistDOList.stream()
                .map(StarSignConvert::blackLimitToStarLimit)
                .collect(Collectors.toMap(StarLimitInfo::getIdCode, v -> v, (v1, v2) -> v1));

        List<UserStarInfo> result = new ArrayList<>();
        for (UserInfo userInfo : userInfos) {
            UserIdCode idCode = userInfo.getIdCode();
            Integer starRole = userInfo.getStarRole();
            StarContractInfo starContractInfo = starContractRepository.getByIdCodeAndType(idCode, starRole);
            UserStarInfo userStarInfo = new UserStarInfo(
                    userInfo.getIdCode(),
                    userInfo,
                    starContractInfo,
                    levelChangeTimeMap.get(idCode),
                    signMap.get(idCode),
                    starLimitInfoMap.get(idCode)
            );
            result.add(userStarInfo);
        }
        return result;
    }

    @Override
    public List<SignStatisticsDetail> listSignStatisticsDetail() {
        Integer lastMonth = DateUtil.formatMonthInt(LocalDateTime.now().minusMonths(1L));
        Integer thisMonth = DateUtil.formatMonthInt(LocalDateTime.now());
        List<SignStatisticsDetailDO> signStatisticsDetailDOList = starSignInfoDOCustomMapper
                .listSignStatisticsDetail(lastMonth, thisMonth);
        if (CollectionUtils.isEmpty(signStatisticsDetailDOList)) {
            return Collections.emptyList();
        }
        return signStatisticsDetailDOList.stream()
                .filter(d -> !testIdCodes.contains(d.getIdCode().intValue()))
                .map(StarSignConvert::SignStatisticsDetailDOToModel)
                .collect(Collectors.toList());
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
